/*
 * Copyright 2007-2010 Jeremy Brown.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @author <a href="http://jeremy-brown.com">Jeremy Brown</a>
 */


package groovy.prefuse.factory

import prefuse.Visualization
import prefuse.data.Graph
import prefuse.data.Table
import prefuse.data.io.*

public class GraphFactory extends AbstractFactory
{
	boolean isLeaf()
	{
		return false
	}
    
    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException
	{
		def file = attributes.remove("file")
		def graphML = attributes.remove("url")
		builder.context.columnNames = attributes.remove("columnNames")
		builder.context.columnTypes = attributes.remove("columnTypes")
		builder.context.columnDefaults = attributes.remove("columnDefaults")
		builder.context.edgeColumnNames = attributes.remove("edgeColumnNames")
		builder.context.edgeColumnTypes = attributes.remove("edgeColumnTypes")
		builder.context.edgeColumnDefaults = attributes.remove("edgeColumnDefaults")

		builder.context.group = builder.pickPrefuseGroup(attributes)   //saved and used only in the case of visualization(){graph()}
		builder.context.predicate = attributes.remove("predicate")	//saved and used only in the case of visualization(){graph()}
		builder.context.visualID = attributes.remove("visualID") //saved and used as the id to save the VisualGraph as

		def graph
				
		//If a Graph object is passed in as the value, simply return it
		if(value instanceof Graph)
		{
			graph = value
		}
		else if(attributes.graph instanceof Graph)
		{
			graph = attributes.remove("graph")
		}
		if(graph)
		{
			changeColumnNames(graph.getNodeTable(), builder.context.columnNames)
			changeColumnNames(graph.getEdgeTable(), builder.context.edgeColumnNames)
			return graph
		}
		
		
		
		//Otherwise we create a Graph (by reading it from a file/url, or by creating an empty Graph)
		
		//File/url name can be passed in as the file: or graphML: attributes
		if(file == null && graphML != null)
		{
			file = graphML
			graphML = null
		}
		

		//Create Graph by reading a file
		if(file != null)
		{
			def graphReader = new GraphMLReader()
			graph = graphReader.readGraph(file)
		}
		
		//Create an empty Graph
		else
		{
			if(attributes.directed)
			{
				graph = new Graph(attributes.remove("directed"))
			}
			else
			{
				graph = new Graph()
			}
			
			//Add node columns
			def columnNames = builder.context.columnNames
			def columnTypes = builder.context.columnTypes
			def columnDefaults = builder.context.columnDefaults
			
			if(columnNames instanceof List && columnTypes instanceof List && (columnNames.size() == columnTypes.size()))
			{
				def table = graph.getNodeTable()
				columnNames.eachWithIndex{columnName, index->
					if(columnDefaults instanceof List && columnDefaults.size() == columnNames.size())
					{
						table.addColumn(columnName, columnTypes[index], columnDefaults[index])
					}
					else
					{
						table.addColumn(columnName, columnTypes[index])
					}
				}
			}
			
			//Add edge columns
			def edgeColumnNames = builder.context.edgeColumnNames
			def edgeColumnTypes = builder.context.edgeColumnTypes
			def edgeColumnDefaults = builder.context.edgeColumnDefaults

			if(edgeColumnNames instanceof List && edgeColumnTypes instanceof List && (edgeColumnNames.size() == edgeColumnTypes.size()))
			{
				def table = graph.getEdgeTable()
				edgeColumnNames.eachWithIndex{edgeColumnName, index->
					if(edgeColumnDefaults instanceof List && edgeColumnDefaults.size() == edgeColumnNames.size())
					{
						table.addColumn(edgeColumnName, edgeColumnTypes[index], edgeColumnDefaults[index])
					}
					else
					{
						table.addColumn(edgeColumnName, edgeColumnTypes[index])
					}
				}
			}

		}
		
		changeColumnNames(graph.getNodeTable(), builder.context.columnNames)
		changeColumnNames(graph.getEdgeTable(), builder.context.edgeColumnNames)
		
		return graph
    }
	
	
	public void changeColumnNames(Table table, List names)
	{
		if(names && names instanceof List)
		{
			names.eachWithIndex{name, index->
				def oldName = table.getColumnName(index)
				if(oldName != name)
				{
					table.m_entries[name] = table.m_entries[oldName]
					table.m_entries.remove(oldName)
					table.m_names[index] = name
				}
			}
		}
	}
	
	
	public void setParent(FactoryBuilderSupport builder, Object parent, Object child)
	{
		if(parent instanceof Visualization)
		{
			def visualGraph
			if(builder.context.group && builder.context.predicate)
			{
				visualGraph = parent.addGraph(builder.context.group, child, builder.context.predicate)
			}
			else if(builder.context.group)
			{
				visualGraph = parent.addGraph(builder.context.group, child)
			}
			
			//Store the visualGraph in the specified visualID (if any)
			if(builder.context.visualID)
			{
				builder.setVariable(builder.context.visualID, visualGraph)
			}
		}
	}


	public void onNodeCompleted(FactoryBuilderSupport builder, Object parent, Object child) 
	{
		//set the column names if they haven't been set yet (or if they've been reset by a table that was loaded later)
		//this only works if the columnNames:[List] and edgeColumnNames:[List] attributes were specified of course
		changeColumnNames(child.getNodeTable(), builder.context.columnNames)
		changeColumnNames(child.getEdgeTable(), builder.context.edgeColumnNames)
    }
}
